package com.smgeek.gkrpc.server;

import com.smgeek.gkrpc.Decoder;
import com.smgeek.gkrpc.Encoder;
import com.smgeek.gkrpc.Request;
import com.smgeek.gkrpc.Response;
import com.smgeek.gkrpc.common.utils.ReflectionUtils;
import com.smgeek.gkrpc.transport.RequestHandler;
import com.smgeek.gkrpc.transport.TransportServer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import java.io.InputStream;
import java.io.OutputStream;
@Slf4j

public class RpcServer {
    private RpcServerConfig config;
    private TransportServer net;
    private Encoder encoder;
    private Decoder decoder;
    private ServiceManager serviceManager;
    private ServiceInvoker serviceInvoker;


    public RpcServer(RpcServerConfig config){
        this.config = config;

        //net
         this.net = ReflectionUtils.newInstance(config.getTransportClass());
         this.net.init(config.getPort(),this.handler);
         this.encoder = ReflectionUtils.newInstance(config.getEncoderClass());
         this.decoder = ReflectionUtils.newInstance(config.getDecoderClass());


         //service
        this.serviceManager = new ServiceManager();
        this.serviceInvoker = new ServiceInvoker();


    }

    public <T> void register(Class<T> interfaceClass,T bean){
        serviceManager.register(interfaceClass,bean);
    }

    public void start(){
        this.net.start();
    }

    public void stop(){
        this.net.stop();
    }

    private RequestHandler handler = new RequestHandler() {
        @Override
        public void onRequest(InputStream recive, OutputStream outputStream) {
            Response response = new Response();
            try {
                byte[] bytes = IOUtils.readFully(recive, recive.available());
                Request request = RpcServer.this.decoder.decoder(bytes, Request.class);
                log.info("get Request: {}", request);

                ServiceInstance sis = serviceManager.lookup(request);
                Object ret = serviceInvoker.invoke(sis, request);
                response.setData(ret);

            } catch (Exception e) {

                log.warn(e.getMessage(), e);
                response.setCode(1);
                response.setMessage("RpcServer got error: "
                        + e.getClass().getName()
                        + " " + e.getMessage());

            } finally {

                try {
                    byte[] encoder = RpcServer.this.encoder.encoder(response);
                    outputStream.write(encoder);
                } catch (Exception e) {
                    log.warn(e.getMessage(), e);

                }
            }
        }

    };
}
